package application.windows;

import application.anno.*;
import application.anno.impl.InjectionImpl;
import application.constant.Constant;
import application.manager.ParasiticManagerStack;
import application.messager.Messager;
import application.parasitic.ParasiticLayout;
import application.util.ReflectionsUtil;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import application.manager.ActivityManager;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * 窗口组件，若要通过注解注册窗口，则窗口的Controller需要继承此类
 */
public class UserInterface {

    public UserInterface() {

    }

    /**
     * 跳转窗口
     *
     * @param userInterface 当前窗口的控制器
     * @param cls           新窗口的控制器类对象
     * @param isHide        是否关闭旧窗口
     */
    public void startUserInterface(UserInterface userInterface, Class<? extends UserInterface> cls, boolean isHide) {
        // 获取并且显示cls对应的窗口
        Stage stage = startUserInterface(cls);
        // 获取窗口管理者
        ActivityManager activityManager = ActivityManager.getInstance();
        // 如果isHide为true则自动关闭旧窗口
        if (isHide) {
            // 获取旧窗口并且关闭
            activityManager.getStage(userInterface.getClass()).hide();
            // 将旧窗口压入回退栈，等待回退弹出。
            activityManager.getPositionStack().backOff(userInterface);
        }
        // 获取窗口左上方的提示文字
        StageTitle stageTitle = cls.getAnnotation(StageTitle.class);
        if (stageTitle != null) {
            // 若StageTitle不为空，则将信息，填入窗口的左上方提示栏中
            stage.setTitle(stageTitle.value());
        }
        // 获取窗口的IconName注解
        IconName iconName = cls.getAnnotation(IconName.class);
        // 获取图片并且加载图片到窗口左上方的图形栏中
        if (iconName != null && !iconName.value().isEmpty()) {
            ObservableList<Image> icons = stage.getIcons();
            if (icons.size() <= 0) {
                icons.add(new Image(getClass().getClassLoader().getResource("image/".concat(iconName.value())).toString()));
            }
        }
    }

    /**
     * 调用此方法关闭当前窗口，若有上一个窗口则回到上一个窗口
     */
    public void end() {
        // 获取窗口对象管理器
        ActivityManager activityManager = ActivityManager.getInstance();
        // 获取窗口栈管理器
        ActivityManager.PositionStack positionStack = activityManager.getPositionStack();
        // 看看是否有窗口在回退栈中等待弹出。
        boolean b = positionStack.hasBack();
        if (b) {
            // 若没有，则弹出关闭程序的提示
            Messager messager = new Messager(Constant.UserInterfaceMessage.NO_HIGHER_LEVEL_ACTION);
            messager.put(Constant.UserInterfaceMessage.NO_HIGHER_LEVEL_FLAG, Constant.UserInterfaceMessage.NO_HIGHER_LEVEL_MESSAGE);
            sendMessage(messager);
            // 并且终止当前窗口销毁方法。
            return;
        }
        // 将当前对窗口推入到前进栈中，等待弹出
        // 并且获取回退栈中等待弹出的窗口。
        UserInterface userInterface = positionStack.forward(this);
        // 获取当前窗口，并且关闭
        activityManager.getStage(this.getClass()).hide();
        // 获取回退栈中等待显示的窗口
        Stage stage = activityManager.getStage(userInterface.getClass());
        if (!stage.isShowing()) {
            // 如果此窗口处于关闭状态，则重新显示。
            stage.show();
        }

        activityManager.unregister(this.getClass());
        unregisterReceiver();
    }

    /**
     * 子类重写此类并且在其上方加上FXML注解，这样可以在加载资源的时候，初始化窗口Controller
     */
    protected void initialize() throws IllegalAccessException {
        Field[] declaredFields = this.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            Group group = field.getAnnotation(Group.class);
            if (group != null) {
                initParasitic(group.value(), field);
            } else if ((field.getAnnotation(Injection.class)) != null) {
                InjectionImpl injection = new InjectionImpl();
                injection.injectionField(this);
            }
        }
        initMessageReceiver();
    }

    /**
     * 调用此方法，向全局发送消息。
     *
     * @param messager 消息携带者
     */
    protected void sendMessage(Messager messager) {
        messager.sendMessage();
    }

    /**
     * 重写此方法用于注册全局消息接收者。
     */
    protected void initMessageReceiver() {

    }

    protected void unregisterReceiver() {

    }

    /**
     * 显示新窗口
     *
     * @param cls 新窗口控制器的类对象
     * @return 新窗口
     */
    private Stage startUserInterface(Class<? extends UserInterface> cls) {
        // 获取窗口管理器
        ActivityManager activityManager = ActivityManager.getInstance();
        // 判断是否已注册了此窗口
        boolean exist = activityManager.isExist(cls);
        if (!exist) {
            // 如果没有注册，则获取此新窗口的Layout注解
            Layout annotation = cls.getAnnotation(Layout.class);
            // 通过注解，获取布局名称
            String value = annotation.value();
            try {
                // 注册新窗口
                activityManager.register(value);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 获取新的窗口界面，并且显示
        activityManager.getStage(cls).show();
        // 返回新的窗口对象
        return activityManager.getStage(cls);
    }

    /**
     * 初始化时，程序会检测当前对象中的所有成员变量中，是否携带了Group注解，若携带了。
     * 若携带了，则会尝试将Group相同的拥有MainLayout注解的Parasitic对象，注入到成员变量中，并且成员变量所表示的位置，与时机上显示Parasitic
     *
     * @param group 组名
     * @param field 携带组名为group的成员变量
     */
    private void initParasitic(String group, Field field) {
        Set<Class<?>> annotationByClass = ReflectionsUtil.getAnnotationByClass(Group.class);
        Set<Class<?>> copy = new HashSet<>();
        copy.addAll(annotationByClass);
        Iterator<Class<?>> iterator = copy.iterator();
        while (iterator.hasNext()) {
            Class<?> cls = iterator.next();
            MainLayout annotation = cls.getAnnotation(MainLayout.class);
            if (annotation == null) {
                continue;
            }
            Group groupFlag = cls.getAnnotation(Group.class);
            if (groupFlag == null) {
                continue;
            }
            if (!group.equals(groupFlag.value())) {
                continue;
            }
            ParasiticManagerStack parasiticManagerStack = ParasiticManagerStack.getInstance();
            try {
                ParasiticLayout parasiticLayout = parasiticManagerStack.register((Class<? extends ParasiticLayout>) cls);
                boolean instanceOf = field.getType().getSuperclass().equals(Pane.class);
                if (instanceOf) {
                    field.setAccessible(true);
                    Pane pane = (Pane) field.get(this);
                    ObservableList<Node> children = pane.getChildren();
                    children.clear();
                    children.add(parasiticLayout.getPane());
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}
